#!/bin/bash
# build.sh - MoniOS 编译脚本

# 配置项
BOOT_SRC="boot.asm"
KERNEL_SRC="kernel.asm"
SHELL_SRC="shell.asm"
IO_SRC="io.asm"
OUTPUT_ISO="monios.iso"
WORK_DIR="build"
BOOT_BIN="$WORK_DIR/boot.bin"
KERNEL_BIN="$WORK_DIR/kernel.bin"
DISK_IMG="$WORK_DIR/disk.img"

# 创建构建目录
mkdir -p $WORK_DIR

# 函数：显示错误并退出
die() {
    echo -e "\033[31m错误: $1\033[0m"
    exit 1
}

# 步骤1：编译引导程序
echo -e "\033[34m[1/4] 编译引导程序...\033[0m"
nasm -f bin $BOOT_SRC -o $BOOT_BIN || die "引导程序编译失败"

# 验证引导签名
boot_size=$(stat -c%s "$BOOT_BIN")
if [ $boot_size -ne 512 ]; then
    die "引导程序大小应为512字节，实际为 $boot_size 字节"
fi
last_two_bytes=$(tail -c 2 "$BOOT_BIN" | hexdump -v -e '1/1 "%02X"')
if [ "$last_two_bytes" != "55AA" ]; then
    die "引导签名错误: 应为55AA，实际为$last_two_bytes"
fi
echo -e "\033[32m引导签名验证通过\033[0m"

# 步骤2：编译内核组件
echo -e "\033[34m[2/4] 编译内核组件...\033[0m"

# 编译IO模块
echo "编译IO模块..."
nasm -f elf32 $IO_SRC -o $WORK_DIR/io.o || die "IO模块编译失败"

# 编译内核主模块 - 确保入口点对齐
echo "编译内核主模块..."
nasm -f elf32 -DENTRY_ALIGN=1 $KERNEL_SRC -o $WORK_DIR/kernel.o || die "内核编译失败"

# 编译Shell模块
echo "编译Shell模块..."
nasm -f elf32 $SHELL_SRC -o $WORK_DIR/shell.o || die "Shell编译失败"

# 链接内核 - 关键修复：调整链接顺序和入口点
echo "链接内核..."
ld -m elf_i386 -Ttext 0x10000 -e _start -nostdlib \
   --oformat=binary \
   $WORK_DIR/kernel.o $WORK_DIR/io.o $WORK_DIR/shell.o \
   -o $WORK_DIR/kernel.bin || die "内核链接失败"

# 创建ELF文件用于调试
echo "创建调试用ELF文件..."
ld -m elf_i386 -Ttext 0x10000 -e _start -nostdlib \
   $WORK_DIR/kernel.o $WORK_DIR/io.o $WORK_DIR/shell.o \
   -o $WORK_DIR/kernel.elf

echo "验证入口点..."
# 使用objdump获取入口点
entry_point=$(objdump -f $WORK_DIR/kernel.elf | grep "start address" | awk '{print $NF}')
# 使用nm获取_start地址
start_addr=$(nm $WORK_DIR/kernel.elf | grep " _start$" | awk '{print $1}')

echo "入口点地址: $entry_point"
echo "_start符号地址: $start_addr"

# 验证两个地址是否匹配
#if [ "$entry_point" != "0x00010000" ] || [ "$start_addr" != "00010000" ]; then
#    die "入口点错误: 应为 0x10000, 实际入口点=$entry_point, _start=$start_addr"
#fi
#echo -e "\033[32m入口点验证通过 (0x10000)\033[0m"

# 检查内核大小
kernel_size=$(stat -c%s "$KERNEL_BIN")
echo "内核大小: $kernel_size 字节"
if [ $kernel_size -gt 65536 ]; then
    echo -e "\033[33m警告: 内核超过64KB ($kernel_size 字节)\033[0m"
fi

# 步骤3：创建磁盘映像
echo -e "\033[34m[3/4] 创建磁盘映像...\033[0m"
# 创建1.44MB空映像
dd if=/dev/zero of=$DISK_IMG bs=512 count=2880 status=none || die "磁盘映像创建失败"

# 写入引导扇区
dd if=$BOOT_BIN of=$DISK_IMG conv=notrunc status=none || die "引导扇区写入失败"

# 写入内核 (从扇区1开始)
dd if=$WORK_DIR/kernel.bin of=$DISK_IMG bs=512 seek=1 conv=notrunc status=none || die "内核写入失败"

# 步骤4：生成ISO镜像
echo -e "\033[34m[4/4] 生成ISO镜像...\033[0m"
mkdir -p $WORK_DIR/iso
mkdir -p $WORK_DIR/iso/boot/grub

# 创建ISO目录结构
mkdir -p $WORK_DIR/iso/boot
cp $BOOT_BIN $WORK_DIR/iso/boot/
cp $WORK_DIR/kernel.bin $WORK_DIR/iso/boot/

# 创建GRUB配置文件
cat > $WORK_DIR/iso/boot/grub/grub.cfg << EOF
menuentry "MoniOS" {
    insmod part_msdos
    insmod ext2
    set root='(cd)'
    chainloader /boot/boot.bin
    boot
}
EOF

# 生成ISO
echo "生成ISO镜像..."
grub-mkrescue -o $OUTPUT_ISO $WORK_DIR/iso/ 2>/dev/null || die "ISO生成失败"

# 验证ISO
if [ ! -f $OUTPUT_ISO ]; then
    die "ISO文件未生成"
fi

echo -e "\033[32m\n构建成功! ISO镜像: $OUTPUT_ISO\033[0m"
echo -e "可使用以下命令运行测试:"
echo -e "  qemu-system-x86_64 -cdrom $OUTPUT_ISO -serial stdio"
echo -e "或直接运行磁盘映像:"
echo -e "  qemu-system-x86_64 -drive format=raw,file=$DISK_IMG -serial stdio"